/** FASSIT (Forms of Selective Attention in Intelligent Transportation Systems)
 * Computational Creativity and Digital Media
 * Cognitive and Media Systems Group
 * Centre for Informatics and Systems of the University of Coimbra (CISUC)
 *
 * Copyright (c) 2010-2013 University of Coimbra, Portugal
 * All rights reserved.
 */
package agent.Simulation;

import java.util.ArrayList;
import java.util.HashMap;

import masterAgent.Knowledge.MasterAgent;
import poi.Info.POI;
import poi.Info.POIwithSAMetrics;
import poi.Surprise.Surprise;
import poi.Uncertainty.Uncertainty;
import agent.Context.Context;
import agent.Knowledge.Agent;
import agent.Metrics.Surprise.SurpriseEvaluator;
import agent.Metrics.Uncertainty.UncertaintyEvaluator;

/**
 * This class is used to Simulate the Agent in a real scenario. The simulation
 * process will be done by considering the Contexts in the list of Contexts
 * 
 * @author Hernani Costa
 * @version 0.2 date: 10/12/2012
 */
public class Simulation {
	private Agent agent;
	private MasterAgent masterAgent;
	private HashMap<Integer, POI> poisNearTheAgent;
	private HashMap<Integer, POIwithSAMetrics> poisNearTheAgentWithMASAValues;
	private HashMap<Integer, POI> noisyPOIs;
	private Surprise agentSurpriseKnowledge;
	private SurpriseEvaluator sEvaluator;
	private Uncertainty agentUncertaintyKnowledge;
	private UncertaintyEvaluator uEvaluator;

	/**
	 * ------------------------------------------------------------------------
	 * Constructor class
	 * ------------------------------------------------------------------------
	 * 
	 * @param context
	 *            - the array list of context that will be used to simulate real
	 *            situations
	 */
	public Simulation(ArrayList<Context> contexts) {
		agent = new Agent();
		masterAgent = new MasterAgent();
		sEvaluator = SurpriseEvaluator.getInstance();
		uEvaluator = UncertaintyEvaluator.getInstance();

		// for all the Contexts used to simulate real situations
		for (Context context : contexts) {
			simualate(context);
			poisNearTheAgent = new HashMap<Integer, POI>();
			poisNearTheAgentWithMASAValues = new HashMap<Integer, POIwithSAMetrics>();
			noisyPOIs = new HashMap<Integer, POI>();
		}
	}

	/**
	 * Simulation Main Class
	 * 
	 * @param context
	 *            - Context
	 */
	private void simualate(Context context) {

		// ------------------------------------------------------------------
		// Asks the MasterAgent for all the POIs that fulfills his context
		// ------------------------------------------------------------------
		poisNearTheAgent = masterAgent.getPOIsNear(context);

		// System.out.println("\n\nPOIS NEAR \n");
		// for (POI pp : poisNearTheAgent.values()) {
		// System.out.println(pp.to_String());
		// }

		// ------------------------------------------------------------------
		// Asks the MasterAgent about the Knowledge it has about these POIs Near
		// (until now, the Surprise associated)
		// ------------------------------------------------------------------
		poisNearTheAgentWithMASAValues = masterAgent
				.getKnowledge(poisNearTheAgent);

		// System.out.println("\n\nPOI Near (with Surprise) \n");
		// for (POIwithSurprise pp :
		// poisNearTheAgentWithMASurpriseValues.values()) {
		// System.out.println(pp.to_String());
		// }

		// ------------------------------------------------------------------
		// Asks the MasterAgent about the Knowledge it has about these POIs
		// Near, in order to store these POIs in the Agent's knowledge.
		// The Master Agent returns this Knowledge with Noise
		// ------------------------------------------------------------------
		noisyPOIs = masterAgent.getNoisyPOIs(poisNearTheAgent, context);

		// System.out.println("\n\nNOISY POIS: " + noisyPOIs.size() + "\n");
		// for (POI pp : noisyPOIs.values()) {
		// System.out.println(pp.to_String());
		// }

		// Iterating all the POIs Near the User. For each POI:
		// 1) we need the Agent's past knowledge about the surprise (an average
		// for all the parameters)
		// 2) we need the real Surprise values from the Master Agent for all the
		// parameters
		// 3) calculate the surprise values for the three parameters (DayOff,
		// Price and Schedule). The Evaluator will confront the
		// Agent's Knowledge with the Master Agent's Knowledge
		// 4) creating the new POI that will e stored in the Agent Knowledge
		// base. It will stored the POI Information as well as the resulted
		// surprise's values for the three parameters

		// 5) POI Information given by the Master Agent that will be stored in
		// the Agent's Knowledge base. This Knowledge has noise!

		int poiID;
		POIwithSAMetrics masterPOISAValues = null, poiWithNewSAValues = null;
		double resultForSurpriseDayOff = 0.0, resultForSurprisePrice = 0.0, resultForSurpriseSchedule = 0.0;
		double resultForUncertaintyDayOff = 0.0, resultForUncertaintyPrice = 0.0, resultForUncertaintySchedule = 0.0;
		ArrayList<POI> newPOIsToBeStored = new ArrayList<POI>();
		POI newPOItoBeStored1 = null;
		POIwithSAMetrics newPOItoBeStored2 = null;

		for (POI poi : poisNearTheAgent.values()) {
			poiID = poi.getPoiID();
			// 1) Agent's past knowledge about the surprise (an average for all
			// the parameters)
			agentSurpriseKnowledge = agent
					.getAgentPastKnowledgeAboutSurprise(context);
			agentUncertaintyKnowledge = agent
					.getAgentPastKnowledgeAboutUncertainty(context);

			// 2) real Surprise values from the Master Agent for all the
			// parameters
			masterPOISAValues = poisNearTheAgentWithMASAValues.get(poiID);
			// System.out.println("A avaliar: ");
			// System.out.println(masterPOISurprise.to_String());

			// 3) calculate the surprise values for the three parameters
			// (DayOff, Price and Schedule). The Evaluator will confront the
			// Agent's Knowledge with the Master Agent's Knowledge
			resultForSurpriseDayOff = sEvaluator.evaluatingDaysOff(
					agentSurpriseKnowledge.getSurpriseDayOff(),
					masterPOISAValues.getSurpriseDayOff());
			resultForSurprisePrice = sEvaluator.evaluatingPrice(
					agentSurpriseKnowledge.getSurprisePrice(),
					masterPOISAValues.getSurprisePrice());
			resultForSurpriseSchedule = sEvaluator.evaluatingSchedule(
					agentSurpriseKnowledge.getSurpriseSchedule(),
					masterPOISAValues.getSurpriseSchedule());

			resultForUncertaintyDayOff = uEvaluator.evaluatingDaysOff(
					agentUncertaintyKnowledge.getUncertaintyDayOff(),
					masterPOISAValues.getUncertaintyDayOff());
			resultForUncertaintyPrice = uEvaluator.evaluatingPrice(
					agentUncertaintyKnowledge.getUncertaintyPrice(),
					masterPOISAValues.getUncertaintyPrice());
			resultForUncertaintySchedule = uEvaluator.evaluatingSchedule(
					agentUncertaintyKnowledge.getUncertaintySchedule(),
					masterPOISAValues.getUncertaintySchedule());

			// 4) creating the new POI that will e stored in the Agent Knowledge
			// base.
			// It will stored the POI Information as well as the resulted
			// surprise's values for the three parameters
			newPOItoBeStored1 = new POI(poi.getPoiName(), poi.getPoiID(),
					poi.getNumCheckins(), poi.getPrice(), poi.getLocation(),
					poi.getCategory(), poi.getSubCategory(), poi.getDaysOn(),
					poi.getDaysOff());
			newPOItoBeStored1.setSurpriseTOdayOff(resultForSurpriseDayOff);
			newPOItoBeStored1.setSurpriseTOprice(resultForSurprisePrice);
			newPOItoBeStored1.setSurpriseTOschedule(resultForSurpriseSchedule);

			newPOItoBeStored1
					.setUncertaintyTOdayOff(resultForUncertaintyDayOff);
			newPOItoBeStored1.setUncertaintyTOprice(resultForUncertaintyPrice);
			newPOItoBeStored1
					.setUncertaintyTOschedule(resultForUncertaintySchedule);

			newPOIsToBeStored.add(newPOItoBeStored1);

			// System.out.println("NEW POI THAT WILL BE STORED IN THE DATABASE:\n"
			// + newPOItoBeStored1.to_String() + "\n"
			// + newPOItoBeStored1.to_StringSurprise());

			// 5) POI Information given by the Master Agent that will be stored
			// in the Agent's Knowledge base. This Knowledge has noise!
			newPOItoBeStored2 = agent.getSAValuesForPOI(newPOItoBeStored1);
			// storing the surprise values
			poiWithNewSAValues = new POIwithSAMetrics(noisyPOIs.get(poiID),
					newPOItoBeStored2.getSurprisePrice(),
					newPOItoBeStored2.getSurpriseDayOff(),
					newPOItoBeStored2.getSurpriseSchedule(),
					newPOItoBeStored2.getUncertaintyPrice(),
					newPOItoBeStored2.getUncertaintyDayOff(),
					newPOItoBeStored2.getUncertaintySchedule());
			
			agent.saveSAValues(poiWithNewSAValues, context);

		}
		// guarda os pois na tabela de pois visitados pelo agente
		agent.savePOIs(newPOIsToBeStored, context);

	}

	/**
	 * ------------------------------------------------------------------------
	 * Used to provide an example that exercises the functionality of the class.
	 * ------------------------------------------------------------------------
	 * 
	 * @param args
	 */
	public static void main(String args[]) {
		SimulationCoordinates sc = new SimulationCoordinates(200);
		ArrayList<Context> ctest = sc.getContextsForTesting();

		// DBConnection connector = new DBConnection();
		// POI poi = connector.getPOI(1507669);
		// Context contextForRun1 = new Context(poi.getLocation(), "Morning",
		// "Sunday", new Goal().getCoffee(), 2 00, 1);
		// ArrayList<Context> contexts = new ArrayList<Context>();
		// contexts.add(contextForRun1);

		Simulation simulation = new Simulation(ctest);

	}
}
